#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <resolv.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <iostream>
#include <openssl/sha.h>
#include <openssl/evp.h>
#include <cstring>
#include <pthread.h>

#include "BasicRingBuffer.hh"
#include "socket.hh"
#include "decoding.hh"
#include "CryptoPrimitive.hh"
/*for the use of LevelDB*/
#include "leveldb/db.h"
/*for the use of Slice*/
#include "leveldb/slice.h"
/*for the use of write batch*/
#include "leveldb/write_batch.h"
/*for the use of cache*/
#include "leveldb/cache.h"
/*for the use of Bloom filter*/
#include "leveldb/filter_policy.h"

/* downloader buffer size */
#define DOWNLOAD_BUFFER_SIZE (1024*1024)
#define MEM_TABLE_SIZE (16<<20)
#define BLOCK_CACHE_SIZE (32<<20)
#define BLOOM_FILTER_KEY_BITS 10
/* downloader ringbuffer size */
#define DOWNLOAD_RB_SIZE 2048
#define FILE_HEAD (-9)
#define BLOCKCP (-8)
#define BLOCKCP_END (-7)
using namespace std;

class Downloader{

 private:
  std::string dbDirName_;
  std::string recipeFileDirName_;
  leveldb::DB *db_;
  leveldb::Options dbOptions_;
  leveldb::ReadOptions readOptions_;

public:
  typedef struct {
    char hf[32];
    }fileHF_t;

  typedef struct {
   int filesize;
   int numOfblock;
   int left;
   }fileMDHead_t;

  typedef struct {
    unsigned char fingerFP[32];
    unsigned char key[32];
    int blockcpid;
    int blockcpSize;
  }blockCpMDHead_t;

  typedef struct {
    blockCpMDHead_t blockcp_header;
    unsigned char* data;
  }blockCpHeadObj_t;

     /* init object for initiating download */
        typedef struct{
            int type;
            char* filename;
            int namesize;
        }init_t;

  typedef struct {
  int type;
  union{
    fileHF_t hfObj;
    fileMDHead_t fileObj;
    blockCpHeadObj_t blockcpObj;
   };
  }Item_t; 
  
  typedef struct {
    int index;
    Downloader* obj;
  }param_uploader;

    /* thread parameter structure */
    typedef struct{
         Downloader* obj;
     }param_t;
  
    Socket* socket_;

    /* signal buffer */
    RingBuffer<init_t>* signalBuffer_;
    RingBuffer<blockCpMDHead_t>* blocpMdHeadBuffer_;
    RingBuffer<Item_t>* ringBuffer_;
    pthread_t tid_;
    Decoder* decoderObj_;
    /* metadata buffer */
    /* container buffer */

    Downloader(int userID,Decoder* obj);
    ~Downloader();
    int indicateEnd();
    //download the file with filename
    int downloadFile(char *filename);
    
    static void* thread_handler(void* param);

};
